package instrumentation;


import java.util.*;


import util.GAConfig;
import util.Log;
import GA.Chromosome;
import Operations.CreateObject;
import Operations.Operation;
import instrumentation.CoverageBranchTarget;

public class TrackerCoverage {

	private static TrackerCoverage tracker;
	// Branch Coverage
	public int[] branches= new int[GAConfig.MAX_BR];
	public int[] hitBranches= new int[GAConfig.MAX_BR];
	
	public List<Operation> preOperation= new LinkedList<Operation>();
	
	public  Chromosome[] branchCoveredByIndividual = new Chromosome[GAConfig.MAX_BR] ;	
	public  Map<Integer,Chromosome> branchNotCoveredByIndividual = new HashMap<Integer, Chromosome>() ;
//	public   Map<Integer, LinkedList<Chromosome>> branchNotCoveredByIndividual = new LinkedHashMap<Integer, LinkedList<Chromosome>>();

	
    private static int maxBRCover = 0;

    
	private double distance ;
	private  CoverageBranchTarget condTarget ;


	private  final Map<Integer,CoverageBranchTarget> trackers = new LinkedHashMap<Integer,CoverageBranchTarget>();

	public Map<Integer,CoverageBranchTarget> getTrackers(){
		return trackers;
	}
	public void setTrackerToNull(){
		tracker=null;
	}
	
    public static TrackerCoverage getTracker() {
		if(tracker == null)
			tracker = new TrackerCoverage();

		return tracker;
	}

	public  TrackerCoverage() {
		reset();
	}

	public void reset() {
		 preOperation= new LinkedList<Operation>();
		 hitBranches= new int[GAConfig.MAX_BR];
		branches= new int[GAConfig.MAX_BR];
		condTarget=null;
		TRACE_ID=0;
		

	}
	
	public  void branchHit(int branchId) {
		if(branchId==1)
			System.out.print("");
		if(!tracker.trackers.containsKey(branchId)){
			condTarget = new CoverageBranchTarget(branchId);
			tracker.trackers.put(branchId,condTarget);
		}
		hitBranches[branchId]++;
	}
	public void branchCovered(int branchId) {
		if(trackers.containsKey(branchId)){
			if(branchId==90)
				System.out.print("");

			branches[branchId]++;
			hitBranches[branchId]=0;
			CoverageBranchTarget tempCondTarget=(CoverageBranchTarget)trackers.get(branchId);
			if(tempCondTarget.getBranchId()==branchId && tempCondTarget.getQuality()!=Float.NEGATIVE_INFINITY)
				tempCondTarget.setDistance(-1);
		}
	}
	
	public void buildSequence_HitBR(Map<String, Class<?>> cluster, Map<String, String[]>  refFactory,Map<String, List<CreateObject>> constructors,Operation op) {
		preOperation.add(op);
		for (int BR = 0; BR < hitBranches.length; BR++) {
//				if(BR==1 &&(CoverageBranchTarget)trackers.get(BR)!=null)
//					if(branchNotCoveredByIndividual.get(BR)!=null)
//						System.out.println("BR: "+ BR+ "size: "+ branchNotCoveredByIndividual.get(BR).getSize());// + " chromosome: "+ branchNotCoveredByIndividual.get(BR));

				condTarget=(CoverageBranchTarget)trackers.get(BR);
				if (condTarget != null) {
					if (condTarget.getQuality() != Float.NEGATIVE_INFINITY) {
						Chromosome existSeq = branchNotCoveredByIndividual.get(BR);
						if(existSeq==null){
							List<Operation> ops = new LinkedList<Operation>();
							ops.addAll(preOperation);
							Chromosome chrom = new Chromosome(cluster,refFactory, constructors,ops);
								chrom.setFitness(condTarget.getQuality());
							branchNotCoveredByIndividual.put(BR, chrom.clone());
						}
						else{
							if(existSeq.getFitness()>condTarget.getQuality()){
								List<Operation> ops = new LinkedList<Operation>();
								ops.addAll(preOperation);
								Chromosome chrom = new Chromosome(cluster,refFactory, constructors,ops);
									chrom.setFitness(condTarget.getQuality());
								branchNotCoveredByIndividual.put(BR, chrom);

							}
							else{
//								if(existSeq.getFitness()==condTarget.getQuality()){
//									if(preOperation.size()<existSeq.getSize()){
//										List<Operation> ops = new LinkedList<Operation>();
//										ops.addAll(preOperation);
//										Chromosome chrom = new Chromosome(cluster,refFactory, constructors,ops);
//											chrom.setFitness(condTarget.getQuality());
//										branchNotCoveredByIndividual.put(BR, chrom);
//									}
//								}
							}
						}
						

						//	System.out.print(ops.toString());
						
					}
				}

			}
//		}
	}
public boolean isBranchHit(int branchID){
	if(hitBranches[branchID]!=0) return true;

	return false;
}
	// Acuriri thesis  x!= null For example, it can return 1 if the two values are different and 0 otherwise.
	public void setConditionTargetDistance(double distance) {
////		System.out.println("distance:"+distance);
		if(condTarget.getQuality()>=0)
			condTarget.setDistance(distance);
	}
	//null==null=> 1(v1=0; v2=0)+1=>1; quality=0.5
	public void calculateConditionTargetDistance(double v1, double v2) {
		distance=(Math.abs(v1 - v2)+1);
		if(condTarget.getQuality()>=0)
			condTarget.setDistance(distance);

	}


	public static int TRACE_ID = 0;
	public static int genTraceID() {
		return TRACE_ID++;
	}
	

	public  int getConditionTargetId(int branchId) {
		if(trackers.containsKey(branchId)){
			 condTarget=(CoverageBranchTarget)trackers.get(branchId);
			if(condTarget.getBranchId()==branchId)
//				if(condTarget.getQuality()>=0){
					return branchId;
//				}
//			else
//				condTarget=null;
		}
//		else
//			condTarget=null;
		return -1;
	}

	public  void setIfCondition(int branchId,String cName,String mName,String mType, 
						String fName_1, String fName_2,String cValue_1,String cValue_2,
						String callee_1,String callee_2,
						String mCall,String mClase
						,String argsField) {
		if(trackers.containsKey(branchId)){
			 condTarget=(CoverageBranchTarget)trackers.get(branchId);
			 Boolean isPublic=true;
			 if(!mType.equals("true"))
				 isPublic=false;
			 condTarget.setIfCondition(cName,mName, isPublic, fName_1, fName_2, cValue_1, cValue_2,callee_1,callee_2,mCall, mClase,argsField);
		}

	}

	public float calculateFitness(Chromosome test) {
		int covTest = 0;
		float brancDistance=0;
		for (int branchId = 0; branchId < branches.length; branchId++) {
			if (branches[branchId] > 0) {
				// total Branch 
				covTest++;
				if(branchId==3)
					System.out.print("");
				if(branchCoveredByIndividual[branchId]==null)
					branchCoveredByIndividual[branchId]=test;
				branchNotCoveredByIndividual.remove(branchId);
			}else{
				//Branch Distance
				condTarget=(CoverageBranchTarget)trackers.get(branchId);
				if(condTarget!=null)
				if(condTarget.getQuality()!=Float.NEGATIVE_INFINITY&&condTarget.getQuality()!=Float.POSITIVE_INFINITY )
						brancDistance+=condTarget.getQuality();

			}
				
		}
		if (covTest > maxBRCover) {
		    maxBRCover = covTest;
		    for (int i = 0; i < branches.length; i++) {
				if (branches[i] > 0)
					branchCoveredByIndividual[i] = test;
			}
		}

		
		brancDistance= 0.5f *(1-brancDistance);
		if(test!=null)
			return (covTest + (0.5f * (1 / (1.0f + test.getSize()))) + brancDistance);
		return 0;
	}
	   public  void summarizeCoverage(String algorithm) {
		   System.out.println("\t ****" +algorithm + " ****");
		   if (GAConfig.SaveData)Log.log ("\t ****" +algorithm + " ****");
		 StringBuilder Str = new StringBuilder();
		HashSet<Chromosome> suiteBR = new HashSet<Chromosome>();
		if (GAConfig.SaveData)Log.log ("");
		
		 ArrayList<Chromosome> l = new ArrayList<Chromosome>(Arrays.asList(branchCoveredByIndividual));
		 l.removeAll(Collections.singleton(null)); 
		 if(l.size()!=0){
			 System.out.println("BR COVERAGE SUMMARY ( " + l.size()  +" ): ");
			 Str.append("BR COVERAGE SUMMARY ( " + l.size()  +" ):\n ");
		 }
		for (int i = 0;   i < GAConfig.MAX_BR; i++)
			if (branchCoveredByIndividual[i] != null) {
				System.out.print("B" + i + " ");
				Str.append("B"+ i + " ");
				suiteBR.add(branchCoveredByIndividual[i]);
			}
		System.out.println();
		
		 System.out.println("BR NOT COVERED SUMMARY ( " + branchNotCoveredByIndividual.size()  +" ): ");
//		 Str.append("BR COVERAGE SUMMARY ( " + branchNotCoveredByIndividual.size()  +" ):\n ");
		for (int i = 0;   i < GAConfig.MAX_BR; i++)
			if (branchNotCoveredByIndividual.get(i) != null) {
				System.out.print("B" + i + ": ");
			//	Str.append("B"+ i + " ");
	//			Chromosome chrom=branchNotCoveredByIndividual.get(i);
	//			System.out.println(chrom.toString());
			}

		Str.append("\n");
		if (GAConfig.SaveData) Log.log (Str.toString());
		if(suiteBR.size()!=0)
			if (GAConfig.SaveData) Log.log("SUMMARY: " + suiteBR.size()+ " tests needed for total branch coverage:");
		for (Iterator<Chromosome> i = suiteBR.iterator(); i.hasNext()&& GAConfig.SaveData;) {
			Log.log("BR Test:" + i.next());
		}
	

	}
	public String displayCondition(){
		List<Integer> keys = new ArrayList<Integer>( this.trackers.keySet());  
		StringBuilder branchs =new StringBuilder();
		for( int i=0; i<keys.size();i++){		
			String predicate;
	//		System.out.print("br=" + i);
			CoverageBranchTarget condTarget=(CoverageBranchTarget)trackers.get(keys.get(i));
			predicate =  condTarget.toString() + "\n";
			branchs.append(predicate.toString());

		}
		return branchs.toString();
	}
	public int getTotalCoveredBranch() {
		int CoveredBR=0;
		List<Integer> keys = new ArrayList<Integer>( this.trackers.keySet());   
		for( int i=0; i<keys.size();i++){			
			CoverageBranchTarget condTarget=(CoverageBranchTarget)trackers.get(keys.get(i));
			if(condTarget.getQuality()==Float.NEGATIVE_INFINITY)
				CoveredBR++;
		}
		
		return CoveredBR;	
	}
	  public static final String lineSep = System.getProperty("line.separator");
	@Override
	public String toString() {
		int CoveredBR=0;
		int unCoveredBR=0,idx=0;

		StringBuilder branchs =new StringBuilder();
		 int [] BRCovered = new int[this.trackers.size()];
		 int [] BRUnCovered = new int[this.trackers.size()];
		List<Integer> keys = new ArrayList<Integer>( this.trackers.keySet());   
		for( int i=0; i<keys.size();i++){			
			CoverageBranchTarget condTarget=(CoverageBranchTarget)trackers.get(keys.get(i));
			if(condTarget.getQuality()==Float.NEGATIVE_INFINITY){
				CoveredBR++;
				BRCovered[idx++]=keys.get(i);
			}
			else{
				BRUnCovered[unCoveredBR]=keys.get(i);
				unCoveredBR++;
			}

			String predicate;
//			predicate = "[branch:" + condTarget.getBranchId() + "] distance: " + condTarget.getQuality() + "\n";
//			branchs.append(predicate.toString());
		}
//		 System.out.println("Total Branches  ( " + keys.size()  +" ): ");
		 branchs.append("Total Branches  ( " + keys.size()  +" ):");
		 branchs.append(lineSep);
//		 System.out.println("BR Covered SUMMARY ( " + CoveredBR  +" ): ");
		 branchs.append("BR Covered SUMMARY ( " + CoveredBR  +" ):");
		 branchs.append(lineSep);
		 if (!GAConfig.Silent) {
			Arrays.sort(BRCovered);
			for (int i = 0; i < BRCovered.length; i++)
				if (BRCovered[i] != 0)
					branchs.append("B" + BRCovered[i] + " ");
			branchs.append(lineSep);
		}
		branchs.append("BR unCovered SUMMARY ( " + unCoveredBR  +" ): ");
		branchs.append(lineSep);
		 if (!GAConfig.Silent) {
			Arrays.sort(BRUnCovered);
			for (int i = 0; i < BRUnCovered.length; i++)
				if (BRUnCovered[i] != 0)
					branchs.append("B" + BRUnCovered[i] + " ");
			branchs.append(lineSep);
		}

		return branchs.toString(); 
	}
	


	
}
